﻿using System;
using System.ComponentModel;
using System.Web.Services;
using System.Reflection;

using BizElements.BusinessLayer;

// Do NOT change this namespace. Otherwise client component (BizElements.BusinessLayer.WsMethodSender) will not function properly.
namespace BizElements.Services
{
    /// <summary>Executes remote procedure calls on received objects.</summary>
    [WebService(Namespace = "http://ElementsFx/BizElements.Services/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    public class WsMethodReceiver : WebService
    {
        public WsMethodReceiver()
        {
            //CODEGEN: This call is required by the ASP.NET Web Services Designer
            InitializeComponent();
        }

        #region Component Designer generated code

        //Required by the Web Services Designer 
        private IContainer components = null;

        /// <summary>Required method for Designer support - do not modify the contents of this method with the code editor.</summary>
        private void InitializeComponent()
        {
        }

        /// <summary>Clean up any resources being used.</summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing && components != null)
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #endregion

        /// <summary>Invokes a method.</summary>
        /// <param name="serializedBusinessObject">Binary serialized object whose definition (class) contains the specified method.</param>
        /// <param name="methodName">Method to be executed.</param>
        /// <param name="serializedMethodParameters">Binary serialized method parameters.</param>
        /// <returns>Changed object serialized with binary formatter.</returns>
        [WebMethod]
        public byte[] InvokeMethod(ref byte[] serializedBusinessObject, string methodName, byte[][] serializedMethodParameters)
        {
            try
            {
                object businessObject = Serializer.DeserializeWithBinaryFormater(serializedBusinessObject);
                object[] methodParameters = Serializer.DeserializeWithBinaryFormater(serializedMethodParameters);
                if (businessObject == null)
                    throw new Exception("Cannot deserialize business object. Make sure you have referenced object's assembly.");

                object retval = ReflectionUtil.InvokeMethod(businessObject, methodName, methodParameters);

                serializedBusinessObject = Serializer.SerializeWithBinaryFormater(businessObject);
                byte[] serializedRetval = Serializer.SerializeWithBinaryFormater(retval);
                return serializedRetval;
            }
            catch (TargetInvocationException e)
            {
                LogError(e.InnerException);
                throw e.InnerException;
            }
            catch (Exception e)
            {
                LogError(e);
                throw;
            }
        }

        /// <summary>Invokes a static method.</summary>
        /// <param name="serializedType">Binary serialized type which contains the specified static method.</param>
        /// <param name="methodName">Method to be executed.</param>
        /// <param name="serializedMethodParameters">Binary serialized method parameters.</param>
        /// <returns>Method's return value serialized with binary formatter.</returns>
        [WebMethod]
        public byte[] InvokeStaticMethod(byte[] serializedType, string methodName, byte[][] serializedMethodParameters)
        {
            try
            {
                Type type = (Type)Serializer.DeserializeWithBinaryFormater(serializedType);
                object[] methodParameters = Serializer.DeserializeWithBinaryFormater(serializedMethodParameters);
                object retval = ReflectionUtil.InvokeStaticMethod(type, methodName, methodParameters);
                byte[] serializedRetval = Serializer.SerializeWithBinaryFormater(retval);
                return serializedRetval;
            }
            catch (TargetInvocationException e)
            {
                LogError(e.InnerException);
                throw e.InnerException;
            }
            catch (Exception e)
            {
                LogError(e);
                throw;
            }
        }

        /// <summary>Invokes a generic method.</summary>
        /// <param name="serializedBusinessObject">Binary serialized object whose definition (class) contains the specified method.</param>
        /// <param name="methodName">Method to be executed.</param>
        /// <param name="serializedMethodParameters">Binary serialized method parameters.</param>
        /// <returns>Changed object serialized with binary formatter.</returns>
        [WebMethod]
        public byte[] InvokeGenericMethod(ref byte[] serializedBusinessObject, string methodName, byte[][] serializedMethodParameters)
        {
            try
            {
                object businessObject = Serializer.DeserializeWithBinaryFormater(serializedBusinessObject);
                object[] methodParameters = Serializer.DeserializeWithBinaryFormater(serializedMethodParameters);
                if (businessObject == null)
                    throw new Exception("Cannot deserialize business object. Make sure you have referenced object's assembly.");

                object retval = ReflectionUtil.InvokeGenericMethod(businessObject, methodName, methodParameters);

                serializedBusinessObject = Serializer.SerializeWithBinaryFormater(businessObject);
                byte[] serializedRetval = Serializer.SerializeWithBinaryFormater(retval);
                return serializedRetval;
            }
            catch (TargetInvocationException e)
            {
                LogError(e.InnerException);
                throw e.InnerException;
            }
            catch (Exception e)
            {
                LogError(e);
                throw;
            }
        }

        /// <summary>Invokes a generic static method.</summary>
        /// <param name="serializedType">Binary serialized type which contains the specified static method.</param>
        /// <param name="methodName">Method to be executed.</param>
        /// <param name="serializedMethodParameters">Binary serialized method parameters.</param>
        /// <returns>Method's return value serialized with binary formatter.</returns>
        [WebMethod]
        public byte[] InvokeGenericStaticMethod(byte[] serializedType, string methodName, byte[][] serializedMethodParameters)
        {
            try
            {
                Type type = (Type)Serializer.DeserializeWithBinaryFormater(serializedType);
                object[] methodParameters = Serializer.DeserializeWithBinaryFormater(serializedMethodParameters);
                object retval = ReflectionUtil.InvokeGenericStaticMethod(type, methodName, methodParameters);
                byte[] serializedRetval = Serializer.SerializeWithBinaryFormater(retval);
                return serializedRetval;
            }
            catch (TargetInvocationException e)
            {
                LogError(e.InnerException);
                throw e.InnerException;
            }
            catch (Exception e)
            {
                LogError(e);
                throw;
            }
        }

        internal static void LogError(Exception error)
        {
            if (error is TargetInvocationException)
            {
                TargetInvocationException targetInvocationError = (TargetInvocationException)error;
                if (targetInvocationError.InnerException != null)
                    error = targetInvocationError.InnerException;
            }

            bool isCritical = false;
            if (error is System.Data.SqlClient.SqlException)
                isCritical = (error as System.Data.SqlClient.SqlException).Class >= 17;
            else if (error is System.Net.WebException)
                isCritical = true;

            if (isCritical)
                Log.CriticalAppError(error);
            else
                Log.AppError(error);
        }
    }
}
